#include "RulerBar.h"
#include "Ruler.h"
#include "GridModel.h"

#include <QPainter>
#include <QPaintEvent>
#include <QFontMetrics>
#include <QDebug>

// https://kernelcoder.wordpress.com/2010/08/25/how-to-insert-ruler-scale-type-widget-into-a-qabstractscrollarea-type-widget/
// http://stackoverflow.com/questions/10027008/separate-layers-like-feature-on-qgraphicsview

namespace LeGraphicsView
{

    // FIXME: Get rid of these magic numbers and calculate them dynamically based
    // on widget height or width and remove sizeHint() overload
    const int RulerBar::BREADTH = 23;
    const int RulerBar::FONT_SIZE = 10;
    const int RulerBar::MAJOR_TICK_HEIGHT = 6;
    const int RulerBar::MINOR_TICK_HEIGHT = 3;

    RulerBar::RulerBar(RulerBar::Alignment alignment, QWidget *parent) :
        QWidget(parent),
        m_gridModel(nullptr),
        m_alignment(alignment),
        m_currentPos(0)
    {
    }

    RulerBar::~RulerBar()
    {

    }

    void RulerBar::setCursorPosition(const QPointF &pos)
    {
        if (m_alignment == Horizontal)
        {
            m_currentPos = pos.x();
        }
        else
        {
            m_currentPos = pos.y();
        }
        update();
    }

    QSize RulerBar::minimumSizeHint() const
    {
        if (m_alignment == Horizontal)
        {
            return QSize(m_gridModel->size().width(), BREADTH);
        }
        else
        {
            return QSize(BREADTH, m_gridModel->size().height());
        }
    }

    RulerBar::Alignment RulerBar::rulerType() const
    {
        return m_alignment;
    }

    void RulerBar::setGridModel(GridModel *model)
    {
        m_gridModel = model;
        update();
    }

    GridModel *RulerBar::gridModel() const
    {
        return m_gridModel;
    }

    void RulerBar::setBackgroundColor(const QColor &color)
    {
        if (m_backgroundColor == color)
        {
            return;
        }
        m_backgroundColor = color;
        update();
    }

    QColor RulerBar::backgroundColor() const
    {
        return m_backgroundColor;
    }

    void RulerBar::setForegroundColor(const QColor &color)
    {
        if (m_foregroundColor == color)
        {
            return;
        }
        m_foregroundColor = color;
        update();
    }

    QColor RulerBar::foregroundColor() const
    {
        return m_foregroundColor;
    }

    void RulerBar::paintEvent(QPaintEvent *event)
    {
        Q_UNUSED(event);

        if (m_gridModel == nullptr)
        {
            return;
        }

        QPainter painter(this);
        painter.setClipRect(event->rect());
        painter.fillRect(event->rect(), m_backgroundColor);
        QPen pen(m_foregroundColor, 0);
        QBrush brush(m_foregroundColor);
        painter.setPen(pen);
        painter.setBrush(brush);

        if (m_alignment == Horizontal)
        {
            auto values = m_gridModel->majorXValues();
            auto ticks = m_gridModel->majorXTicks();
            for (int i = 0; i < ticks.count(); i++)
            {
                drawMajorTick(painter, ticks[i], values[i]);
            }
            ticks = m_gridModel->minorXTicks();
            for (int i = 0; i < ticks.count(); i++)
            {
                drawMinorTick(painter, ticks[i]);
            }
            drawIndicator(painter, m_gridModel->xTickPosition(m_currentPos));
        }
        else
        {
            auto values = m_gridModel->majorYValues();
            auto ticks = m_gridModel->majorYTicks();
            for (int i = 0; i < ticks.count(); i++)
            {
                drawMajorTick(painter, ticks[i], values[i]);
            }
            ticks = m_gridModel->minorYTicks();
            for (int i = 0; i < ticks.count(); i++)
            {
                drawMinorTick(painter, ticks[i]);
            }
            drawIndicator(painter, m_gridModel->yTickPosition(m_currentPos));
        }
    }

    void RulerBar::drawMajorTick(QPainter &painter, int pixelPos, qreal logicalPos)
    {
        QFont font = painter.font();
        font.setPixelSize(FONT_SIZE);
        painter.setFont(font);
        QString text = QString("%1").arg(logicalPos);
        QFontMetrics fontMetrics(font);
        int textWidth = fontMetrics.width(text);
        if (m_alignment == Horizontal)
        {
            painter.drawText(QPointF(pixelPos - textWidth / 2, FONT_SIZE + 2), text);
            painter.drawLine(QPointF(pixelPos, (BREADTH - 1) - MAJOR_TICK_HEIGHT),
                             QPointF(pixelPos, (BREADTH - 1)));
        }
        else
        {
            painter.save();
            painter.translate(FONT_SIZE + 2, pixelPos + textWidth / 2);
            painter.rotate(-90);
            painter.drawText(0, 0, text);
            painter.restore();
            painter.drawLine(QPointF((BREADTH - 1) - MAJOR_TICK_HEIGHT, pixelPos),
                             QPointF((BREADTH - 1),                     pixelPos));
        }
    }

    void RulerBar::drawMinorTick(QPainter &painter, int pixelPos)
    {
        if (m_alignment == Horizontal)
        {
            painter.drawLine(QPointF(pixelPos, (BREADTH - 1) - MINOR_TICK_HEIGHT),
                             QPointF(pixelPos, (BREADTH - 1)));
        }
        else
        {
            painter.drawLine(QPointF((BREADTH - 1) - MINOR_TICK_HEIGHT, pixelPos),
                             QPointF((BREADTH - 1),                     pixelPos));
        }
    }

    void RulerBar::drawIndicator(QPainter &painter, int pixelPos)
    {
        static const int cursorWidth = 6;
        static const int cursorHeight = 3;

        QPolygon shape;
        if (m_alignment == Horizontal)
        {
            shape << QPoint(-(cursorWidth >> 1), 0)
                  << QPoint(+(cursorWidth >> 1), 0)
                  << QPoint(0,                 cursorHeight - 1);
            shape.translate(pixelPos, (BREADTH - 1) - MAJOR_TICK_HEIGHT + 1);
        }
        else
        {
            shape << QPoint(0,              +(cursorWidth >> 1))
                  << QPoint(0,              -(cursorWidth >> 1))
                  << QPoint(cursorHeight - 1, 0);
            shape.translate((BREADTH - 1) - MAJOR_TICK_HEIGHT + 1, pixelPos);
        }
        painter.drawPolygon(shape);
    }

}
